package lattice;

import java.util.ArrayList;
import java.util.List;

import lattice.traveler.State;
import lattice.traveler.SyncedStateCut;
import lattice.traveler.VectorClock;


/**
 * Computation (global state) is a 2D array of events. Each event represents a
 * version of a shared object and is composed of a vector clock and a state.
 * The clock indicates the "Happen-before" relationship between two events and
 * the state stores the data for performing predicate detection.
 */
public class Computation implements Evaluable {
	private List<State>[] states;
	private Evaluable target;
	
	/**
	 * Initialize a computation with specific number of shared object
	 * @param pNum
	 */
	public Computation(int oNum) {
		states = new List[oNum];
		for (int i = 0; i < oNum; ++i) states[i] = new ArrayList();
	}
	
	/**
	 * Initialize a computation with specific number of shared object and user
	 * defined predicate
	 * @param pNum
	 */
	public Computation(int oNum, Evaluable target) {
		this(oNum);
		setPredicate(target);
	}
	
	/**
	 * Returns the number of total shared object
	 * @return number of total shared object
	 */
	public int objNum() { return states.length; }
	
	/**
	 * Returns the number of state of object o
	 * @param oid the index of object o
	 * @return number of state of object o
	 */
	public int stateNum(int oid) { return states[oid].size(); }
	
	/**
	 *  Returns the vector clock of the specific event
	 * @param oid the index of object o
	 * @param eid the index of the event in object o
	 * @return the vector clock of the specific event in object o
	 */
	public VectorClock getClock(int oid, int eid) {
		return states[oid].get(eid).clock;
	}
	
	/**
	 * Returns the state of the specific event
	 * @param oid the index of object o
	 * @param eid the index of the event in object o
	 * @return the state object of the specific event in object o
	 */
	public Object getData(int oid, int eid) {
		return states[oid].get(eid).data;
	}
	
	/**
	 * Assign the list of vector clocks and states of the specified object o
	 * @param oid the index of object o
	 * @param clocks the list of vector clocks
	 * @param states the list of states
	 */
	public void addState(int oid, State e) {
		states[oid].add(e);
	}
	
	/**
	 * Set the rule for detecting a certain predicate in the computation
	 * @param target the rule for predicate detection
	 */
	public void setPredicate(Evaluable target) {
		this.target = target;
	}
	
	/**
	 * Detect predicate for the specified state cut
	 * @param cut the consistent state in the computation
	 * @return true if the states on the cut satisfies user specified predicate
	 */
	public boolean evaluate(SyncedStateCut cut) {
		ConsistentStates cs = new ConsistentStates();
		for (int i = 0; i < cut.size(); ++i)
			cs.addState(cut.getVid(i), getData(i, cut.getVid(i)));
		return evaluate(cs);
	}
	
	@Override
	public boolean evaluate(ConsistentStates cs) { return target.evaluate(cs); }
}

